Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 <<   zurück
Visual Basic 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual Basic 2005

Visual Basic 2005
1.233 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-585-1
gp Kapitel 7 Weitere Möglichkeiten von Visual Basic 2005
  gp 7.1 Operatorüberladung
    gp 7.1.1 Die Syntax der Operatorüberladung
    gp 7.1.2 Beispiel einer Operatorüberladung
    gp 7.1.3 Überladung der Operatoren »True« und »False«
    gp 7.1.4 Benutzerdefinierte Konvertierungen
  gp 7.2 Collections (Auflistungen)
    gp 7.2.1 Die elementaren Schnittstellen der Auflistungsklassen
    gp 7.2.2 Die Schnittstelle »IList«
    gp 7.2.3 Die Klasse »ArrayList«
    gp 7.2.4 Das Sortieren der Elemente einer »ArrayList«
    gp 7.2.5 Die Schnittstelle »IDictionary«
    gp 7.2.6 Die Klasse »Hashtable«
    gp 7.2.7 Die Klassen »Queue« und »Stack«
    gp 7.2.8 Objektauflistungen im Überblick
    gp 7.2.9 Benutzerdefinierte Auflistungen
  gp 7.3 Generics
    gp 7.3.1 Ein paar allgemeine Worte
    gp 7.3.2 Die Typproblematik am Beispiel der Klasse »Stack«
    gp 7.3.3 Die Lösung mit einer generischen Klasse
    gp 7.3.4 Typparameter mit Constraints einschränken
    gp 7.3.5 Generische Methoden
    gp 7.3.6 Generics und Vererbung
    gp 7.3.7 Generische Klassen in der .NET-Klassenbibliothek
  gp 7.4 Fortgeschrittene Delegat-Techniken
    gp 7.4.1 Eine Beispielanwendung
    gp 7.4.2 Multicast-Delegaten
  gp 7.5 Attribute
    gp 7.5.1 Das »Flags«-Attribut
    gp 7.5.2 Anmerkungen zu den Attributen
    gp 7.5.3 Benutzerdefinierte Attribute


Galileo Computing

7.3 Generics  downtop


Galileo Computing

7.3.1 Ein paar allgemeine Worte  downtop

Eine der vielleicht wichtigsten Ergänzungen der Sprache Visual Basic 2005 in der Version 2.0 sind die Generics. Generics ermöglichen die Verwendung von zur Entwicklungszeit unbekannten Datentypen innerhalb von Klassen, Strukturen, Schnittstellen und Methoden. Sie eignen sich insbesondere für Anwendungsfälle, in denen Collections eine Rolle spielen.

Collections sind im Vergleich zu den Arrays mit einem Nachteil behaftet: die fehlende Typisierung. Wie Sie gesehen haben, lässt sich eine ArrayList zwar sehr gut als Datenspeicher verwenden, aber es kann nicht sichergestellt werden, dass von der ArrayList ausschließlich ein bestimmter Datentyp gespeichert wird. Um das Problem zu lösen, mussten typisierte Collections bisher explizit implementiert werden. Dabei wurden Entwickler von den beiden Klassen CollectionBase und DictionaryBase unterstützt. Wie Sie weiter oben gesehen haben, ist die Codierung einer typisierten Klasse auf Grundlage einer dieser beiden Basisklassen allerdings nicht trivial. Mit Generics ändert sich das, denn die Typisierung wird wesentlich vereinfacht.


Galileo Computing

7.3.2 Die Typproblematik am Beispiel der Klasse »Stack«  downtop

Der folgende Code zeigt die benutzerdefinierte Klasse Stack mit den beiden Methoden Push und Pop. Die internen Daten werden in einem Object-Array gespeichert. Push erwartet in seinem Parameter ebenfalls Object, und Pop liefert Object an den Aufrufer zurück.


Public Class Stack
Private ReadOnly size As Integer
Private elements() As Object
Private pointer As Integer = 0
Public Sub New(ByVal anzahl As Integer)
Me.size = anzahl – 1
ReDim elements(size)
End Sub
Public Sub Push(ByVal element As Object)
If (pointer > Me.size) Then
Throw New StackOverflowException()
End If
elements(pointer) = element
pointer += 1
End Sub
Public Function Pop() As Object
pointer -= 1
If (pointer >= 0) Then
Return elements(pointer)
Else
pointer = 0
Throw New InvalidOperationException("Der Stack ist leer")
End If
End Function
Public ReadOnly Property Length() As Integer
Get
Return Me.pointer
End Get
End Property
End Class

Instanziiert werden kann diese Klasse nur über den Aufruf des einfach parametrisierten Konstruktors, dem ein Integer übergeben wird, der die maximale Anzahl der Elemente im Stack beschreibt. Mit Push wird der Stack-Instanz ein Objekt übergeben. Sollte zu diesem Zeitpunkt die Kapazität des Stacks bereits ausgeschöpft sein, ist eine Ausnahme die Folge. Genauso reagiert auch die Methode Pop, falls sich kein Element mehr auf dem Stack befindet.

Grundsätzlich können sowohl Referenz- als auch Wertetypen auf den Stack geschoben werden. Handelt es sich um Wertetypen, müssen die Daten während des Push-Vorgangs geboxt werden. Beim Auslesen aus dem Stack kommt es zum Unboxing, was sich in einer schlechten Performance bemerkbar macht. Neben diesem Nachteil lässt sich auch noch ein weiterer erkennen: Die Typüberprüfung kann nicht mehr vom Compiler durchgeführt werden, sondern verlagert sich in die Laufzeit. Das folgende Codefragment soll das verdeutlichen:


Option Strict On
Dim stack As Stack = New Stack(10)
stack.Push(2)
Dim str As ClassA = CType(stack.Pop(), ClassA)

Dieser Code wird bei der Konvertierung zu einem Laufzeitfehler führen, weil das zuletzt auf den Stack gelegte Element vom Typ Integer ist und zur Laufzeit der Anwendung versucht wird, den Integer in den Typ ClassA zu casten.

Wie Sie sehen können, birgt die Flexibilität des Typs Object in sich gravierende Nachteile. Sie könnten natürlich versuchen, mehrere Klassen zu entwickeln, die auf einen bestimmten Datentyp spezialisiert sind, z.  B.:


Public Class StackInt
Private elemente() As Integer
Public Sub Push(ByVal number As Integer)
...
End Sub
...
End Class

Die Typsicherheit wäre damit gewährleistet, daran besteht kein Zweifel. Andererseits führt dieser Ansatz möglicherweise zu einer größeren Anzahl ähnlicher Klassen, die jeweils nur für einen spezifischen Typ geeignet sind. Solche Lösungen sind zwar machbar, allerdings auch schlecht zu warten. Zudem könnte sich in naher oder ferner Zukunft der Bedarf nach einem weiteren Stack mit einem noch nicht berücksichtigten Typ ergeben, so dass man rückblickend mit dem Ergebnis nicht zufrieden wäre. Genau an dieser Stelle spielen Generics ihre ganze Stärke aus.


Galileo Computing

7.3.3 Die Lösung mit einer generischen Klasse  downtop

Generics erlauben die Verwendung von unbekannten Datentypen. Dazu wird an Stelle eines konkreten Datentyps eine Art Platzhalter angegeben. Dieser wird von der Klasse typisiert benutzt. Die Platzhalter werden in runden Klammern hinter dem neuen Schlüsselwort Of angegeben und innerhalb der Klasse wie reguläre Datentypen verwendet.


' ---------------------------------------------------------
' Beispiel: ...\Kapitel 7\GenericDemo
' ---------------------------------------------------------
Public Class Stack(Of T)
Private ReadOnly size As Integer
Private elements() As T
Private pointer As Integer = 0
Public Sub Push(ByVal element As T) ...
Public Function Pop() As T ...
End Class

Nutzen Sie eine generische Klasse wie Stack(Of T), und teilen Sie dem Compiler mit, durch welchen Datentyp T ersetzt werden soll. T wird auch als generischer Typparameter bezeichnet. In der folgenden Anweisung handelt es sich um Integer:


Dim s As Stack(Of Integer) = New Stack(Of Integer)(10)

Alle Methoden der Klasse, die T als Parameter definieren oder zurückgeben, akzeptieren jetzt nur noch Integerwerte. Den Zugriff auf die generische Klasse Stack(Of T) zeigt nachfolgend die Methode Main. Der Code ist in einen Try-Block gefasst, um ausgelöste Ausnahmen behandeln zu können. In Kapitel 9 werden wir das Thema Ausnahmen (Exceptions) noch ausgiebig behandeln.


Sub Main()
Try
Dim stack As Stack(Of Integer) = _
New Stack(Of Integer)(3)
stack.Push(123)
stack.Push(4711)
stack.Push(34)
Dim i As Integer
For i = stack.Length To 1 Step –1
Console.WriteLine(stack.Pop())
Next
stack.Pop()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Console.ReadLine()
End Sub

Mehrere Typparameter

Die zuvor verwendete generische Klasse Stack benötigte einen variablen Datentyp. Je nachdem, wie die zu entwickelnde Klasse aussehen soll, kann der Bedarf an Platzhaltern jedoch variieren. Ein typisches Beispiel dazu wäre eine Dictionary-Klasse, die nicht nur hinsichtlich des Schlüssels, sondern auch hinsichtlich des zugeordneten Werts individuell typisiert werden soll. In solchen Fällen erlaubt Visual Basic 2005 die Angabe mehrerer Platzhalter, die innerhalb der runden Klammer mittels Kommata getrennt werden.


Public Class Dictionary(Of K, V)
Public Sub Add(ByVal key As K, ByVal value As V)
...
End Sub
End Class

Der Zugriff auf eine Klasse mit mehreren Typparametern erfolgt genauso wie oben am Beispiel der Klasse Stack gezeigt. Jedem typisierten Parameter wird ein bekannter Typ in runden Klammern übergeben:


Dim myDict As Dictionary(Of Integer, ClassA) = _
New Dictionary(Of Integer, ClassA)()
myDict.Add(1, New ClassA())


Galileo Computing

7.3.4 Typparameter mit Constraints einschränken  downtop

Mit der Definition


Public Class GenericClass(Of T)
...
End Class

teilen wir dem Compiler mit, dass der spätere Datentyp zum Zeitpunkt der Klassenentwicklung noch völlig unbekannt ist. Es kann jeder x-beliebige Datentyp verwendet werden.

Wollen Sie innerhalb des Codes der generischen Klasse ein bestimmtes Klassenmitglied des verwendeten Typs aufrufen (beispielsweise eine Methode), ist eine explizite und damit auch unsichere Konvertierung notwendig. Eventuelle Fehler, weil der verwendete Datentyp dieses Klassenmitglied nicht veröffentlicht, würden erst zur Laufzeit der Anwendung erkannt.

Um die Problematik zu verstehen, sehen Sie sich das folgende Beispiel an. Die Klasse SortedElements(Of T) hat eine Methode Add, mit der ein Element der Auflistung hinzugefügt wird. Das neue Element soll nach typspezifischen Kriterien einsortiert werden. Dazu ist ein Vergleich mit den schon enthaltenen Elementen notwendig, zu dem die Methode CompareTo der Schnittstelle IComparable genutzt wird.


Public Class SortedElements(Of T)
Dim arr(100) As T
Public Function Add(ByVal element As T) As T()
Dim i As Integer
For i = 0 To arr.Length – 1
Dim result As Integer = _
CType(element, IComparable).CompareTo(arr(i))
...
Next
...
End Function
End Class

Implementiert der Datentyp IComparable nicht, wird eine Ausnahme ausgelöst. Um diesem Problem aus dem Weg zu gehen, lassen sich die generische Typparameter mit einer Einschränkung versehen, wie im Folgenden gezeigt wird:


Public Class SortedElements(Of T As IComparable)
...
End Class

Nun ist eine Bedingung festgelegt, an die sich der spätere konkrete Typ halten muss: Er muss die aufgeführte Schnittstelle IComparable unterstützen. Wird im Code ein Typ angegeben, der diese Schnittstelle nicht unterstützt, meldet der Compiler einen Fehler.

Eine Bedingung ist nicht auf Schnittstellen beschränkt; Sie können auch eine Klasse angeben und legen damit die Basisklasse des an den Typparameter T übergebenen konkreten Typs fest.

Mehrere Constraints definieren

Typparameter, die keine Einschränkungen aufweisen, werden als ungebundene Typparameter bezeichnet, mit Einschränkungen als gebundene Typparameter. Im Bedarfsfall dürfen Sie auch mehrere Einschränkungen angeben, die durch geschweifte Klammern hinter As angegeben werden.


Public Class SortedElements(Of T _
As {IComparable, IDisposable, BaseClass})
...
End Class

Der Konstruktor-Constraint »New«

Nehmen wir an, Sie möchten in einer generischen Klasse ein Objekt vom Typ eines generischen Typparameters erzeugen. Das Problem dabei ist, dass der VB-Compiler nicht weiß, ob der Typparameter einen passenden Konstruktor hat. Die Folge wäre ein Kompilierfehler.

Um in dieser Situation eine Lösung zu bieten, können Sie als Einschränkung New angeben, wie im folgenden Codefragment gezeigt wird:


Public Class ClassA(Of T As New)
...
Public Sub New()
Dim obj As New T
End Sub
End Class

Sie treffen damit eine entscheidende Aussage: Der gewählte Argumenttyp muss einen öffentlichen, parameterlosen Konstruktor unterstützen. Einen parametrisierten Konstruktor vorzuschreiben ist nicht möglich.


Galileo Computing

7.3.5 Generische Methoden  downtop

Generische Typen sind nicht nur im Zusammenhang mit Klassen möglich, sondern auch mit Methoden. Dabei ist es nicht zwingend notwendig, dass die Typparameter einer Methode denen der Klasse entsprechen:


Public Class ClassA(Of T)
Public Sub GenericMethod(Of K)(obj As K)
...
End Sub
End Class

Im Gültigkeitsbereich der Klasse ist in diesem Fall der Typ T bekannt, K nur innerhalb der Methode.

Sie dürfen generische, methodenspezifische Typparameter auch angeben, wenn die Klasse selbst keine definiert:


Public Class ClassA
Public Sub GenericMethod(Of T)(ByVal x As T)
Console.WriteLine("Operation in generischer Methode")
End Sub
End Class

Der Aufruf einer Methode mit generischen Typparametern ist sehr einfach. Sie instanziieren in gewohnter Weise zuerst die Klasse und rufen die Methode unter Angabe des gewünschten konkreten Datentyps auf:


Dim obj As New ClassA
obj.GenericMethod(Of Integer)(23)

Sie können sogar auf die Typangabe verzichten, denn auch in diesem Fall wird der VB-Compiler die richtige Schlussfolgerung ziehen:


obj.GenericMethod(25)

Dieser Aufruf ist absolut gleichwertig.

Methoden und Constraints

Muss der generische Typparameter einer Methode bestimmten Bedingungen genügen, legen Sie einen Constraint fest. Die Syntax entspricht der der Constraints einer Klasse. Allerdings ist es nicht möglich, einen Constraint für einen generischen Typparameter einer Methode zu definieren, der bereits auf Klassenebene festgelegt ist.


Galileo Computing

7.3.6 Generics und Vererbung  downtop

Generische Klassen können abgeleitet werden. Die Regeln sind ähnlich denen, die wir schon kennen. Aufgrund der besonderen Natur generischer Klassen sind dabei jedoch ein paar Besonderheiten zu beachten.

Ist die Basisklasse generisch, kann die abgeleitete Klasse den generischen Typparameter übernehmen und selbst generisch sein.


Public Class ClassA(Of T)
...
End Class
Public Class ClassB(Of T)
Inherits ClassA(Of T)
...
End Class

Soll die abgeleitete Klasse nicht generisch sein, muss der Typparameter in der Angabe der Basisklasse durch einen konkreten Datentyp ersetzt werden, wie nachfolgend gezeigt:


Public Class ClassA(Of T As IComparable)
...
End Class
Public Class ClassB
Inherits ClassA(Of Integer)
...
End Class

Sie können umgekehrt auch dann eine generische Subklasse entwickeln, wenn die Basisklasse nicht generisch ist.


Galileo Computing

7.3.7 Generische Klassen in der .NET-Klassenbibliothek  toptop

Die Generics haben erst in der Version 2.0 ihren Einzug in das .NET Framework gefunden und die Klassenbibliothek beeinflusst. Beispielsweise enthält .NET 2.0 mit System.Collections.Generic einen neuen Namespace, der eine Reihe generischer Auflistungen beinhaltet. Dieser Namespace enthält unter anderem auch eine Klasse Stack(OF T), ähnlich der, die wir anfangs unserer Ausführungen zu den Generics beschrieben haben. Altbekannte Klassen des Frameworks 1.0/1.1 sind um generische Methoden ergänzt worden. Hier sei exemplarisch nur die Klasse Array erwähnt.

Viele andere Klassen und Schnittstellen des Namespace System.Collections finden ein generisches Pendant in System.Collections.Generic. In der folgenden Tabelle sind die wichtigsten Klassen und Schnittstellen des neuen Namespace aufgeführt nebst den nichtgenerischen Implementierungen.


Tabelle 7.6     Generische Klassen und Schnittstellen und ihre Pendants

System.Collections.Generic System.Collections
Collection<T> CollectionBase
Dictionary<K, V> Hashtable
IComparer<T> IComparer
IComparable<T> IComparable
IEnumerable<T> IEnumerable
IList<T> IList
List<T> ArrayList
Queue<T> Queue
SortedDictionary<K, V> SortedList
Stack<T> Stack

 <<   zurück
  
  Zum Katalog
Zum Katalog: Visual Basic 2005
Visual Basic 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Visual C# 2005






 Visual C# 2005


Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Das Programmierhandbuch SQL Server 2005






 Das Programmier-
 handbuch
 SQL Server 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de